package model

import (
	"database/sql"
	"fmt"
	"strings"

	"github.com/tal-tech/go-zero/core/stores/builder"
	"github.com/tal-tech/go-zero/core/stores/cache"
	"github.com/tal-tech/go-zero/core/stores/sqlc"
	"github.com/tal-tech/go-zero/core/stores/sqlx"
	"github.com/tal-tech/go-zero/core/stringx"
)

var (
	waterUserFieldNames          = builder.RawFieldNames(&WaterUser{})
	waterUserRows                = strings.Join(waterUserFieldNames, ",")
	waterUserRowsExpectAutoSet   = strings.Join(stringx.Remove(waterUserFieldNames, "`user_id`", "`create_time`", "`update_time`"), ",")
	waterUserRowsWithPlaceHolder = strings.Join(stringx.Remove(waterUserFieldNames, "`user_id`", "`create_time`", "`update_time`"), "=?,") + "=?"

	cacheMygoWaterUserUserIdPrefix = "cache:mygo:waterUser:userId:"
	cacheMygoWaterUserUserNamePrefix = "cache:mygo:waterUser:userName:"
)

type (
	WaterUserModel interface {
		Insert(data *WaterUser) (sql.Result, error)
		FindOne(userId int64) (*WaterUser, error)
		Update(data *WaterUser) error
		Delete(userId int64) error
		FindOneByMobile(phonenumber string) (*WaterUser, error)
		FindOneByUsername(userName string) (*WaterUser, error)
	}

	defaultWaterUserModel struct {
		sqlc.CachedConn
		table string
	}

	WaterUser struct {
		UserId      int64          `db:"user_id"`     // 用户ID
		UserName    string         `db:"user_name"`   // 用户账号
		NickName    string         `db:"nick_name"`   // 用户昵称
		UserType    string         `db:"user_type"`   // 用户类型（00系统用户）
		Email       string         `db:"email"`       // 用户邮箱
		Phonenumber string         `db:"phonenumber"` // 手机号码
		Sex         string         `db:"sex"`         // 用户性别（0男 1女 2未知）
		Avatar      string         `db:"avatar"`      // 头像地址
		Home        string         `db:"home"`        // 家庭住址(用户定位)
		Place       string         `db:"place"`       // 所属区域(对应home表里面的id)
		Password    string         `db:"password"`    // 密码
		Status      string         `db:"status"`      // 帐号状态（0正常 1停用）
		DelFlag     string         `db:"del_flag"`    // 删除标志（0代表存在 2代表删除）
		LoginIp     string         `db:"login_ip"`    // 最后登录IP
		LoginDate   sql.NullTime   `db:"login_date"`  // 最后登录时间
		CreateBy    string         `db:"create_by"`   // 创建者
		CreateTime  sql.NullTime   `db:"create_time"` // 创建时间
		UpdateBy    string         `db:"update_by"`   // 更新者
		UpdateTime  sql.NullTime   `db:"update_time"` // 更新时间
		Remark      sql.NullString `db:"remark"`      // 备注
	}
)

func NewWaterUserModel(conn sqlx.SqlConn, c cache.CacheConf) WaterUserModel {
	return &defaultWaterUserModel{
		CachedConn: sqlc.NewConn(conn, c),
		table:      "`water_user`",
	}
}

func (m *defaultWaterUserModel) Insert(data *WaterUser) (sql.Result, error) {
	query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, waterUserRowsExpectAutoSet)
	ret, err := m.ExecNoCache(query, data.UserName, data.NickName, data.UserType, data.Email, data.Phonenumber, data.Sex, data.Avatar, data.Home, data.Place, data.Password, data.Status, data.DelFlag, data.LoginIp, data.LoginDate, data.CreateBy, data.UpdateBy, data.Remark)

	return ret, err
}

func (m *defaultWaterUserModel) FindOne(userId int64) (*WaterUser, error) {
	mygoWaterUserUserIdKey := fmt.Sprintf("%s%v", cacheMygoWaterUserUserIdPrefix, userId)
	var resp WaterUser
	err := m.QueryRow(&resp, mygoWaterUserUserIdKey, func(conn sqlx.SqlConn, v interface{}) error {
		query := fmt.Sprintf("select %s from %s where `user_id` = ? limit 1", waterUserRows, m.table)
		return conn.QueryRow(v, query, userId)
	})
	switch err {
	case nil:
		return &resp, nil
	case sqlc.ErrNotFound:
		return nil, ErrNotFound
	default:
		return nil, err
	}
}

func (m *defaultWaterUserModel) Update(data *WaterUser) error {
	mygoWaterUserUserIdKey := fmt.Sprintf("%s%v", cacheMygoWaterUserUserIdPrefix, data.UserId)
	_, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
		query := fmt.Sprintf("update %s set %s where `user_id` = ?", m.table, waterUserRowsWithPlaceHolder)
		return conn.Exec(query, data.UserName, data.NickName, data.UserType, data.Email, data.Phonenumber, data.Sex, data.Avatar, data.Home, data.Place, data.Password, data.Status, data.DelFlag, data.LoginIp, data.LoginDate, data.CreateBy, data.UpdateBy, data.Remark, data.UserId)
	}, mygoWaterUserUserIdKey)
	return err
}

func (m *defaultWaterUserModel) FindOneByMobile(mobile string) (*WaterUser, error) {
	baseMemberMobileKey := fmt.Sprintf("%s%v", cacheMygoWaterUserUserIdPrefix, mobile)
	var resp WaterUser
	err := m.QueryRowIndex(&resp, baseMemberMobileKey, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
		query := fmt.Sprintf("select %s from %s where phonenumber = ? limit 1", waterUserRows, m.table)
		if err := conn.QueryRow(&resp, query, mobile); err != nil {
			return nil, err
		}
		return resp.UserId, nil
	}, m.queryPrimary)
	switch err {
	case nil:
		return &resp, nil
	case sqlc.ErrNotFound:
		return nil, ErrNotFound
	default:
		return nil, err
	}
}

func (m *defaultWaterUserModel) FindOneByUsername(userName string) (*WaterUser, error) {
	mygoWaterUserUserIdKey := fmt.Sprintf("%s%v", cacheMygoWaterUserUserNamePrefix, userName)
	var resp WaterUser
	err := m.QueryRow(&resp, mygoWaterUserUserIdKey, func(conn sqlx.SqlConn, v interface{}) error {
		query := fmt.Sprintf("select %s from %s where `user_name` = ? limit 1", waterUserRows, m.table)
		return conn.QueryRow(v, query, userName)
	})
	switch err {
	case nil:
		return &resp, nil
	case sqlc.ErrNotFound:
		return nil, ErrNotFound
	default:
		return nil, err
	}
}

func (m *defaultWaterUserModel) Delete(userId int64) error {

	mygoWaterUserUserIdKey := fmt.Sprintf("%s%v", cacheMygoWaterUserUserIdPrefix, userId)
	_, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
		query := fmt.Sprintf("delete from %s where `user_id` = ?", m.table)
		return conn.Exec(query, userId)
	}, mygoWaterUserUserIdKey)
	return err
}

func (m *defaultWaterUserModel) formatPrimary(primary interface{}) string {
	return fmt.Sprintf("%s%v", cacheMygoWaterUserUserIdPrefix, primary)
}

func (m *defaultWaterUserModel) queryPrimary(conn sqlx.SqlConn, v, primary interface{}) error {
	query := fmt.Sprintf("select %s from %s where `user_id` = ? limit 1", waterUserRows, m.table)
	return conn.QueryRow(v, query, primary)
}
